QUnit.module("rules");

QUnit.test("rules() - internal - input", function (assert) {
    var element = $("#firstname");

    $("#testForm1").validate();

    assert.deepEqual(element.rules(), {required: true, minlength: 2});
});

QUnit.test("rules(), ignore method:false", function (assert) {
    var element = $("#firstnamec");

    $("#testForm1clean").validate({
        rules: {
            firstnamec: {required: false, minlength: 2}
        }
    });

    assert.deepEqual(element.rules(), {minlength: 2});
});

QUnit.test("rules() HTML5 required (no value)", function (assert) {
    var element = $("#testForm11text1");

    $("#testForm11").validate();

    assert.deepEqual(element.rules(), {required: true});
});

QUnit.test("rules() - internal - select", function (assert) {
    var element = $("#meal");

    $("#testForm3").validate();

    assert.deepEqual(element.rules(), {required: true});
});

QUnit.test("rules() - external", function (assert) {
    var element = $("#text1");

    $("#form").validate({
        rules: {
            action: {date: true, min: 5}
        }
    });

    assert.deepEqual(element.rules(), {date: true, min: 5});
});

QUnit.test("rules() - external - complete form", function (assert) {
    assert.expect(1);

    var methods = $.extend({}, $.validator.methods),
        messages = $.extend({}, $.validator.messages),
        v;

    $.validator.addMethod("verifyTest", function () {
        assert.ok(true, "method executed");
        return true;
    });
    v = $("#form").validate({
        rules: {
            action: {verifyTest: true}
        }
    });
    v.form();

    $.validator.methods = methods;
    $.validator.messages = messages;
});

QUnit.test("rules() - internal - input", function (assert) {
    var element = $("#form8input");

    $("#testForm8").validate();

    assert.deepEqual(element.rules(), {required: true, number: true, rangelength: [2, 8]});
});

QUnit.test("rules(), merge min/max to range, minlength/maxlength to rangelength", function (assert) {
    jQuery.validator.autoCreateRanges = true;

    $("#testForm1clean").validate({
        rules: {
            firstnamec: {
                min: -15,
                max: 0
            },
            lastname: {
                minlength: 0,
                maxlength: 10
            }
        }
    });

    assert.deepEqual($("#firstnamec").rules(), {range: [-15, 0]});
    assert.deepEqual($("#lastnamec").rules(), {rangelength: [0, 10]});

    jQuery.validator.autoCreateRanges = false;
});

QUnit.test("rules(), guarantee that required is at front", function (assert) {
    $("#testForm1").validate();
    var v = $("#v2").validate(),
        v1 = $("#subformRequired").validate();

    function flatRules(element) {
        var result = [];
        jQuery.each($(element).rules(), function (key) {
            result.push(key);
        });
        return result.join(" ");
    }

    assert.equal(flatRules("#firstname"), "required minlength");
    assert.equal(flatRules("#v2-i6"), "required minlength maxlength");
    assert.equal(flatRules("#co_name"), "required maxlength");

    v.destroy();
    v1.destroy();
    jQuery.validator.autoCreateRanges = true;
    v = $("#v2").validate();
    assert.equal(flatRules("#v2-i6"), "required rangelength");

    v1 = $("#subformRequired").validate({
        rules: {
            co_name: "required"
        }
    });
    $("#co_name").removeClass();
    assert.equal(flatRules("#co_name"), "required maxlength");
    jQuery.validator.autoCreateRanges = false;
});

QUnit.test("rules(), evaluate dynamic parameters", function (assert) {
    assert.expect(2);

    $("#testForm1clean").validate({
        rules: {
            firstnamec: {
                min: function (element) {
                    assert.equal($("#firstnamec")[0], element);
                    return 12;
                }
            }
        }
    });

    assert.deepEqual($("#firstnamec").rules(), {min: 12});
});

QUnit.test("rules(), class and attribute combinations", function (assert) {

    $.validator.addMethod("customMethod1", function () {
        return false;
    }, "");
    $.validator.addMethod("customMethod2", function () {
        return false;
    }, "");

    $("#v2").validate({
        rules: {
            "v2-i7": {
                required: true,
                minlength: 2,
                customMethod: true
            }
        }
    });

    assert.deepEqual($("#v2-i1").rules(), {required: true});
    assert.deepEqual($("#v2-i2").rules(), {required: true, email: true});
    assert.deepEqual($("#v2-i3").rules(), {url: true});
    assert.deepEqual($("#v2-i4").rules(), {required: true, minlength: 2});
    assert.deepEqual($("#v2-i5").rules(), {required: true, minlength: 2, maxlength: 5, customMethod1: "123"});
    jQuery.validator.autoCreateRanges = true;
    assert.deepEqual($("#v2-i5").rules(), {required: true, customMethod1: "123", rangelength: [2, 5]});
    assert.deepEqual($("#v2-i6").rules(), {required: true, customMethod2: true, rangelength: [2, 5]});
    jQuery.validator.autoCreateRanges = false;
    assert.deepEqual($("#v2-i7").rules(), {required: true, minlength: 2, customMethod: true});

    delete $.validator.methods.customMethod1;
    delete $.validator.messages.customMethod1;
    delete $.validator.methods.customMethod2;
    delete $.validator.messages.customMethod2;
    delete $.validator.classRuleSettings.customMethod2;
});

QUnit.test("rules(), dependency checks", function (assert) {
    var v = $("#testForm1clean").validate({
            rules: {
                firstnamec: {
                    min: {
                        param: 5,
                        depends: function (el) {
                            return ( /^a/ ).test($(el).val());
                        }
                    }
                },
                lastname: {
                    max: {
                        param: 12
                    },
                    email: {
                        depends: function () {
                            return true;
                        }
                    }
                }
            }
        }),
        rules = $("#firstnamec").rules();

    assert.equal(v.objectLength(rules), 0);

    $("#firstnamec").val("ab");
    assert.deepEqual($("#firstnamec").rules(), {min: 5});

    assert.deepEqual($("#lastnamec").rules(), {max: 12, email: true});
});

QUnit.test("rules(), add and remove", function (assert) {
    $.validator.addMethod("customMethod1", function () {
        return false;
    }, "");
    $("#v2").validate();
    $("#v2-i5").removeClass("required").removeAttr("minlength maxlength");
    assert.deepEqual($("#v2-i5").rules(), {customMethod1: "123"});

    $("#v2-i5").addClass("required").attr({
        minlength: 2,
        maxlength: 5
    });
    assert.deepEqual($("#v2-i5").rules(), {required: true, minlength: 2, maxlength: 5, customMethod1: "123"});

    $("#v2-i5").addClass("email").attr({min: 5});
    assert.deepEqual($("#v2-i5").rules(), {
        required: true,
        email: true,
        minlength: 2,
        maxlength: 5,
        min: 5,
        customMethod1: "123"
    });

    $("#v2-i5").removeClass("required email").removeAttr("minlength maxlength customMethod1 min");
    assert.deepEqual($("#v2-i5").rules(), {});

    delete $.validator.methods.customMethod1;
    delete $.validator.messages.customMethod1;
});

QUnit.test("rules(), add and remove static rules", function (assert) {

    $("#testForm1clean").validate({
        rules: {
            firstnamec: "required date"
        }
    });

    assert.deepEqual($("#firstnamec").rules(), {required: true, date: true});

    $("#firstnamec").rules("remove", "date");
    assert.deepEqual($("#firstnamec").rules(), {required: true});
    $("#firstnamec").rules("add", "email");
    assert.deepEqual($("#firstnamec").rules(), {required: true, email: true});

    $("#firstnamec").rules("remove", "required");
    assert.deepEqual($("#firstnamec").rules(), {email: true});

    assert.deepEqual($("#firstnamec").rules("remove"), {email: true});
    assert.deepEqual($("#firstnamec").rules(), {});

    $("#firstnamec").rules("add", "required email");
    assert.deepEqual($("#firstnamec").rules(), {required: true, email: true});

    assert.deepEqual($("#lastnamec").rules(), {});
    $("#lastnamec").rules("add", "required");
    $("#lastnamec").rules("add", {
        minlength: 2
    });
    assert.deepEqual($("#lastnamec").rules(), {required: true, minlength: 2});

    var removedRules = $("#lastnamec").rules("remove", "required email");
    assert.deepEqual($("#lastnamec").rules(), {minlength: 2});
    $("#lastnamec").rules("add", removedRules);
    assert.deepEqual($("#lastnamec").rules(), {required: true, minlength: 2});
});

QUnit.test("rules(), add messages", function (assert) {
    $("#firstnamec").attr("title", null);
    var v = $("#testForm1clean").validate({
        rules: {
            firstnamec: "required"
        }
    });
    $("#testForm1clean").valid();
    $("#firstnamec").valid();
    assert.deepEqual(v.settings.messages.firstname, undefined);

    $("#firstnamec").rules("add", {
        messages: {
            required: "required"
        }
    });

    $("#firstnamec").valid();
    assert.deepEqual(v.errorList[0] && v.errorList[0].message, "required");

    $("#firstnamec").val("test");
    $("#firstnamec").valid();
    assert.equal(v.errorList.length, 0);
});

QUnit.test("rules(), rangelength attribute as array", function (assert) {
    $("#testForm13").validate();
    assert.deepEqual($("#cars-select").rules(), {
        required: true,
        rangelength: [2, 3]
    });
});

QUnit.test("rules(), global/local normalizer", function (assert) {
    var username = $("#usernamec"),
        urlc = $("#urlc"),
        lastname = $("#lastnamec"),
        v;

    username.val("\t\t \r");
    urlc.val("");

    v = $("#testForm1clean").validate({

        // Using the normalizer to trim the value of all elements before validating them.
        normalizer: function (value) {

            // This normalizer should only be called for the username element, and nothing else.
            assert.notEqual(this, urlc[0], "This normalizer should not be called for urlc element.");
            assert.equal(this, username[0], "`this` in this normalizer should be the username element.");

            // Trim the value of the input
            return $.trim(value);
        },

        rules: {
            username: {
                required: true
            },
            urlc: {
                required: true,
                url: true,

                // Using the normalizer to append https:// if it's not
                // present on the input value
                normalizer: function (value) {
                    assert.equal(this, urlc[0], "`this` in the normalizer should be the urlc element.");

                    var url = value;

                    // Check if it doesn't start with http:// or https:// or ftp://
                    if (url && url.substr(0, 7) !== "http://" &&
                        url.substr(0, 8) !== "https://" &&
                        url.substr(0, 6) !== "ftp://") {

                        // Then prefix with http:// or even https://
                        url = "https://" + url;
                    }

                    // Return the new url
                    return url;
                }
            },
            lastname: {
                required: true,
                normalizer: function (value) {
                    assert.equal(this, lastname[0], "`this` in the normalizer should be the lastname element.");

                    // Return null in order to make sure an exception is thrown
                    // when normalizer returns a non string value.
                    value = null;

                    return value;
                }
            }
        }
    });

    // Validate only the username and the url elements.
    username.valid();
    assert.equal(v.invalidElements()[0], username[0], "The username should be invalid");

    urlc.valid();
    assert.equal(v.invalidElements()[0], urlc[0], "The url should be invalid");

    assert.equal(v.numberOfInvalids(), 2, "There is two invalid elements");

    username.val("something");
    urlc.val("google.com");

    username.trigger("keyup");
    urlc.trigger("keyup");

    assert.equal(v.numberOfInvalids(), 0, "All elements are valid");
    assert.equal(v.size(), 0, "All elements are valid");

    // Validate the lastname element, which will throw an exception
    assert.throws(function () {
        v.check(lastname[0]);
    }, function (err) {
        return err.name === "TypeError" && err.message === "The normalizer should return a string value.";
    }, "This should throw a TypeError exception.");
});

QUnit.test("rules() - on unexpected input", function (assert) {
    var emptySet = $("#firstname .mynonexistantclass"),
        nonFormElement = $("div#foo"),
        result;

    result = emptySet.rules("add", "whatever");
    assert.deepEqual(result, undefined, "can work on an empty set without a js error");

    result = nonFormElement.rules("add", "whatever");
    assert.deepEqual(result, undefined, "can work on a non-form element");
});
